/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 *  @filename: process.cc
 */

#include<server_includes.h>

#include <process.h>
#include <debug.h>
#include <database.h>
#include <read-config.h>


#include <mutex.h>


#include <gettext.h>
#include <error.h>
#include <errmsg_print.h>



using namespace BackEnd ;

///initialize the static variable
int ProcessManager::noThreads=0;


Mutex ProcessManager::prcMgr_mutex;
///The system database address.
caddr_t ProcessManager::sysAddr         =0;
///The userdatabase address.
caddr_t ProcessManager::usrAddr         =0;
///The global pointer to the systemDatabase.
Database* ProcessManager::systemDatabase=NULL;

BackEnd::List<void> ProcessManager::hasLockList;


void ThreadInfo::init(pid_t _pid,pthread_t _threadid)
{
    ///PID
    pid_ = _pid;
    /// pthread id.
    thrid_ =_threadid;
    /// mutex
    want_mtx = NULL;

    for (int i =0; i <MAX_MUTEX_PER_THREAD; i++) has_mtx[i] = NULL;
    /// init the struct of ThreadTrans Array.
    for (int i =0; i <Conf::config.getMaxProcs(); i++)  thrTrans_[i].init();

}


void ThreadTrans::print()
{
    if (pid_ ==0) return;
    printf("<THREADTRANS>\n");
    printf("  <PID> %d </PID>\n", pid_);
    printf("  <THRID> %lu </THRID>\n", thrid_);
    //printf("  <TRANSACTION> %x </TRANSACTION>\n");
    printf("</THREADTRANS>\n");
}
void ThreadInfo::print()
{  /*
    printf("<THREADINFO>\n");
    printf("  <PID> %d </PID>\n", pid_);
    printf("  <THRID> %lu </THRID>\n", thrid_);
    //printf("  <WAIT> %x </WAIT>\n", want_);
    printf("  <MUTEXLIST>\n");
    for (int i =0; i <MAX_MUTEX_PER_THREAD; i++)
         if (has_[i]) printf("<MUTEX> %x </MUTEX>\n", has_[i]);
    printf("  </MUTEXLIST>\n");
    printf("  <TRANSLIST>\n");
    for (int i =0; i <MAX_THREADS_PER_PROCESS; i++) thrTrans_[i].print();
    printf("  </TRANSLIST>\n");
    printf("</THREADINFO>\n");
    */

}

//It does not check for re registering as well as deregistering unregistered threads.
//as it is handled in the connection class open and close methods.
BackEnd::DbRetVal ProcessManager::registerThread()
{

    BackEnd::DbRetVal rv = systemDatabase->getProcessTableMutex(false);

    if (OK != rv)
        return rv;
    pid_t pid  = OS::getpid();
    pthread_t thrid = OS::getthrid();

    ThreadInfo* pInfo = systemDatabase->getThreadInfo(0);
    int i;
    int _max_procs = Conf::config.getMaxProcs();
    for (i=0; i < _max_procs; i++)
    {
        /// We have found one slot which has pid_ is zero
        /// then we memorize the i.
        if (pInfo->pid_ == 0 ) break;
        pInfo++;
    }
    if ( i == _max_procs)
    {
      systemDatabase->releaseProcessTableMutex(false);
      return (DbRetVal)HA_ERR_SYS_OUT_OF_RESOURCE;
    }
    pInfo->init(pid,thrid);
    procSlot = i;

    errmsg_printf(ERRMSG_LVL_DBUG,_("[%s] [%d] Process [%d] Thread [%lu] registered with slot %d.\n")
                                     ,__FILE__,__LINE__,pid, thrid, procSlot);
    prcMgr_mutex.getLock(-1, false);
        noThreads++;
    prcMgr_mutex.releaseLock(-1, false);
    systemDatabase->releaseProcessTableMutex(false);

    return BackEnd::OK;

}
BackEnd::DbRetVal ProcessManager::deregisterThread(int _procSlot)
{
    if(_procSlot <0 ) return BackEnd::ER_BadArg ;
    BackEnd::DbRetVal rv = systemDatabase->getProcessTableMutex(false);
    if (OK != rv)
      return rv;


    ThreadInfo* pInfo = systemDatabase->getThreadInfo(_procSlot);
    Transaction *trans = ProcessManager::getThreadTransaction(_procSlot);

    if( !pInfo){
      errmsg_printf(ERRMSG_LVL_ERROR, _("Can not get ThreadInfo at slot [%d].\n"),_procSlot);
      goto DEREGISTER_THREAD;
    }
    if (NULL != trans)
    {
       if (trans->status_ == TransRunning)
       {
       }
    }
    if (pInfo->want_mtx != NULL)
    {
        systemDatabase->releaseProcessTableMutex(false);
        return (DbRetVal)HA_ERR_SYS_INTERNAL;
    }
    for (int muti = 0 ;muti < MAX_MUTEX_PER_THREAD; muti++)
    {
        if (pInfo->has_mtx [muti] !=  NULL)
        {
            pInfo->has_mtx[muti]->releaseLock(_procSlot);
            systemDatabase->releaseProcessTableMutex(false);
            return (DbRetVal)HA_ERR_SYS_INTERNAL;
        }
    }
    errmsg_printf(ERRMSG_LVL_DBUG,_("[%s] [%d] Process [%d] Thread [%lu] deregistered from slot %d.\n")
                                         ,__FILE__,__LINE__,pInfo->pid_,pInfo->thrid_, _procSlot);
    pInfo->reset ();

    prcMgr_mutex.getLock(-1, false);
    noThreads--;
    prcMgr_mutex.releaseLock(-1, false);
DEREGISTER_THREAD:
    systemDatabase->releaseProcessTableMutex(false);
    return BackEnd::OK;
}

DbRetVal ProcessManager::addMutex(Mutex *mut, int pslot)
{
    if (systemDatabase == NULL)
        return OK;

    ThreadInfo* pInfo = systemDatabase->getThreadInfo(pslot);
    int i=0;
    /*for (; i < Conf::config.getMaxProcs(); i++)
    {
        if (pInfo->pid_ == pid && pInfo->thrid_ == thrid) break;
        pInfo++;
    }
    if (i == Conf::config.getMaxProcs())
    {
       return ErrSysFatal;
    }*/
    for (i = 0 ;i < MAX_MUTEX_PER_THREAD; i++)
    {
        if (pInfo->has_mtx[i] ==  NULL)
        {
            pInfo->has_mtx [i] = mut;
            return BackEnd::OK;
        }
    }

    return (DbRetVal)HA_ERR_SYS_INTERNAL;
}

BackEnd::DbRetVal ProcessManager::removeMutex(Mutex *mut, int pslot)
{

    if (systemDatabase == NULL)
    {
        return BackEnd::OK;
    }

    ThreadInfo* pInfo = systemDatabase->getThreadInfo(pslot);
    int i=0;
    /*for (; i < Conf::config.getMaxProcs(); i++)
    {
        if (pInfo->pid_ == pid && pInfo->thrid_ == thrid) break;
        pInfo++;
    }
    if (i == Conf::config.getMaxProcs())
    {
        return ErrSysFatal;
    }*/
    for ( i = 0 ;i < MAX_MUTEX_PER_THREAD; i++)
    {
      if (pInfo->has_mtx[i] ==  mut)
      {
         pInfo->has_mtx[i] = NULL;
         return BackEnd::OK;
      }
    }
    return (DbRetVal)HA_ERR_SYS_INTERNAL;
}

BackEnd::DbRetVal ProcessManager::setThreadTransaction(Transaction *trans,
                                                       int          pslot)
{
    pid_t pid = OS::getpid();
    pthread_t thrid = OS::getthrid();

    if (!systemDatabase)
      return BackEnd::OK;

    int _max_procs = Conf::config.getMaxProcs();
    ThreadInfo* pInfo = systemDatabase->getThreadInfo(pslot);
    int i=0;

    for (i = 0 ;i < _max_procs; i++)
    {
        if (pInfo->thrTrans_[i].pid_ != 0) continue;
    }
    if (i == _max_procs)
    {
      return (DbRetVal)HA_ERR_SYS_OUT_OF_RESOURCE;
    }
    pInfo->thrTrans_[i].pid_   = pid;
    pInfo->thrTrans_[i].thrid_ = thrid;
    pInfo->thrTrans_[i].trans_ = trans;

    return OK;
}

Transaction* ProcessManager::getThreadTransaction(int pslot)
{
    pid_t pid = OS::getpid();
    pthread_t thrid = OS ::getthrid();

    if (systemDatabase == NULL)
        return NULL;

    int _max_procs = Conf::config.getMaxProcs();
    ThreadInfo* pInfo = systemDatabase->getThreadInfo(pslot);
    int i=0;

    for (i = 0; i < _max_procs; i++)
    {
        if (pInfo->thrTrans_[i].pid_ == pid
         && pInfo->thrTrans_[i].thrid_ == thrid)
            break;
    }
    if (i == _max_procs)
            return NULL;


    return pInfo->thrTrans_[i].trans_;
}

Transaction** ProcessManager::getThreadTransAddr(int pslot)
{
    pid_t pid       = OS::getpid();
    pthread_t thrid = OS::getthrid();

    if (!systemDatabase)
      return NULL;


    ThreadInfo* pInfo = systemDatabase->getThreadInfo(pslot);
    int i=0;
    int _max_procs = Conf::config.getMaxProcs();
    for ( i = 0 ;i < _max_procs; i++)
    {
      if (pInfo->thrTrans_[i].pid_ == pid
            && pInfo->thrTrans_[i].thrid_ == thrid)
            break;
    }
    if (i == _max_procs)
        return NULL;


    return &pInfo->thrTrans_[i].trans_;
}




void ProcessManager::printUsageStatistics()
{
    ThreadInfo* pInfo = systemDatabase->getThreadInfo(0);
    int i=0, usedCount =0 , freeCount =0;
    for (; i < Conf::config.getMaxProcs(); i++)
    {
        if (pInfo->pid_ != 0 ) usedCount++; else freeCount++;
        pInfo++;
    }
    printf("<ProcTable>\n");
    printf("  <UsedSlots> %d </UsedSlots>\n", usedCount);
    printf("  <FreeSlots> %d </FreeSlots>\n", freeCount);
    printf("</ProcTable>\n");

}

void ProcessManager::printDebugInfo()
{
    printf("<ProcTable>\n");
    ThreadInfo* pInfo = systemDatabase->getThreadInfo(0);
    int i=0, usedCount =0 , freeCount =0;
    for (; i < Conf::config.getMaxProcs(); i++)
    {
        if (pInfo->pid_ != 0 ) {pInfo->print(); usedCount++;}
        else freeCount++;
        pInfo++;
    }
    printf("<UsedSlots> %d </UsedSlots>\n", usedCount);
    printf("<FreeSlots> %d </FreeSlots>\n", freeCount);
    printf("</ProcTable>\n");
}


bool ProcessManager::isAnyOneRegistered()
{
    if (!systemDatabase)
        return false;

    //the process which calls this will have an entry in proc table.
    //so checking for 1
    ThreadInfo* pInfo = systemDatabase->getThreadInfo(0);
    int i=0, usedCount =0;
    for (; i < Conf::config.getMaxProcs(); i++)
    {
        if (pInfo->pid_ != 0 ) usedCount++;
        pInfo++;
    }
    if (usedCount == 1) return false;
    else return true;
}
